#! /usr/bin/env python

import logging, random
logging.getLogger("scapy").setLevel(1)
#logger = logging.getLogger("scapy")
#logger.setLevel(logging.DEBUG)
#fh = logging.FileHandler('fuzz.log')
#fh.setLevel(logging.DEBUG)
#formatter = logging.Formatter('%(asctime)s - %(name)s -%(levelname)s - %(message)s')
#fh.setFormatter(formatter)
#logger.addHandler(fh)


from scapy import *
from modLib import *

# IP for all transmissions
ip = IP(dst="192.168.2.78")
random.seed()

#log to file
import sys
sys.stdout = open('fuzz.log', 'w')

#Array of valid PDUs to cycle through
#Grouped according to fields
startAddr_PDUs = [ModbusPDU01_Read_Coils(), ModbusPDU02_Read_Discrete_Inputs(), ModbusPDU03_Read_Holding_Registers(), ModbusPDU04_Read_Input_Registers(), ModbusPDU0F_Write_Multiple_Coils(), ModbusPDU10_Write_Multiple_Registers()]

quantity_PDUs = [ModbusPDU01_Read_Coils(), ModbusPDU02_Read_Discrete_Inputs(), ModbusPDU03_Read_Holding_Registers(), ModbusPDU04_Read_Input_Registers(), ModbusPDU0F_Write_Multiple_Coils(), ModbusPDU10_Write_Multiple_Registers()]

Addr_PDUs = [ModbusPDU05_Write_Single_Coil(), ModbusPDU06_Write_Single_Register()]

Value_PDUs = [ModbusPDU05_Write_Single_Coil(), ModbusPDU06_Write_Single_Register()]

byteCount_PDUs = [ModbusPDU0F_Write_Multiple_Coils(), ModbusPDU10_Write_Multiple_Registers()]

outputsValue_PDUs = [ModbusPDU0F_Write_Multiple_Coils(), ModbusPDU10_Write_Multiple_Registers()]


# Sets up the session with a TCP three-way handshake
# Send the syn, receive the syn/ack
tcp = TCP( flags = 'S', window = 65535, sport = RandShort(), dport = 502, options = [('MSS', 1360 ), ('NOP', 1), ('NOP', 1), ('SAckOK', '')])
synAck = sr1 ( ip / tcp )

# Send the ack
tcp.flags = 'A'
tcp.sport = synAck[TCP].dport
tcp.seq = synAck[TCP].ack
tcp.ack = synAck[TCP].seq + 1
tcp.options = ''
send( ip / tcp )

def default_send(pdu):

    tcp = TCP( flags = 'AP', window = 65535, sport = synAck[TCP].ack, dport = 502, options = '')
    tcp.sport = synAck[TCP].dport
    tcp.seq = synAck[TCP].ack
    tcp.ack = synAck[TCP].seq + 1

    adu = ModbusADU() / pdu
    tcp = tcp / adu
    #tcp.show()
    data = sr1(( ip / tcp), timeout = 2)
    #print data.ToString()#encode("ascii")
    try:
        data.show()
    except AttributeError:
        pass
    #At some point log everything... not quite sure how to do that yet
    time.sleep(2)


#first send off the boundry values for each field
print("Fuzzing startAddr")
for pdu in startAddr_PDUs:
        pdu.startAddr = 0x0
        default_send(pdu)

        pdu.startAddr = 0xff
        default_send(pdu)

print("Fuzzing quantity")
for pdu in quantity_PDUs:
        pdu.quantity = 0x0
        default_send(pdu)
	pdu.quantity = 0xff
        default_send(pdu)

print("Fuzzing Addr")
for pdu in Addr_PDUs:
        pdu.Addr = 0x0
        default_send(pdu)

        pdu.Addr = 0xff
        default_send(pdu)

print("Fuzzing Value")
for pdu in Value_PDUs:
        pdu.Value = 0x0
        default_send(pdu)

        pdu.Value = 0xff
        default_send(pdu)

print("Fuzzing byteCount")
for pdu in byteCount_PDUs:
        pdu.byteCount = 0x0
        default_send(pdu)

        pdu.byteCount = 0xff
        default_send(pdu)

print("Fuzzing outputsValue")
for pdu in outputsValue_PDUs:
        pdu.outputsValue

		#Now go HAM fuzzing random things for random function codes
while(True):
        #pick a random number
        field = random.randrange(6)
        #fuzz the corresponding function code in a random way
        if field == 1:
                #pick random function code from this category
                pdu = startAddr_PDUs[random.randrange(6)]
                pdu.startAddr = random.randrange(0xffff)
                default_send(pdu)

        if field == 1:
                pdu = quantity_PDUs[random.randrange(6)]
                pdu.quantity = random.randrange(0xffff)
                default_send(pdu)

        if field == 2:
                pdu = Addr_PDUs[random.randrange(2)]
                pdu.Addr = random.randrange(0xffff)
                default_send(pdu)

        if field == 3:
                pdu = Value_PDUs[random.randrange(2)]
                pdu.Value = random.randrange(0xffff)
                default_send(pdu)

        if field == 4:
                pdu = byteCount_PDUs[random.randrange(2)]
                pdu.byteCount = random.randrange(0xffff)
                default_send(pdu)
	if field == 5:
                pdu = outputsValue_PDUs[random.randrange(2)]
                #pdu.outputsValue = 

raw_input()

# Creates and sends the Modbus Read Holding Registers command packet
# Send the ack/push i.e. the request, receive the data i.e. the response
#tcp.flags = 'AP'
#adu = ModbusADU()
#pdu1 = ModbusPDU05_Write_Single_Coil()
#pdu1.outputValue = 0xFF00
#adu1 = adu / pdu1
#tcp1 = tcp / adu1
#===================================
#pdu2 = ModbusPDU05_Write_Single_Coil()
#pdu2.outputValue = 0x0000
#adu2 = adu / pdu2
#tcp2 = tcp / adu2

#tcp.show()
#while(True):
#       data1 = sr1(( ip / tcp1 ), timeout = 2)
        #data1.show()
#       print("On")
#       time.sleep(2)
#       data2 = sr1(( ip / tcp2 ), timeout = 2)
        #data2.show()
#       print("Off")
#       time.sleep(2)
#data

# Acknowledges the response
# Ack the data response
# TODO: note, the 17 below should be replaced with a read packet length method...
tcp.flags = 'A'
tcp.seq = data[TCP].ack
#tcp.ack = data[TCP]# + 17
tcp.payload = ''
finAck = sr1( ip / tcp )
#finAck.show()

